//
//	File Name   :	DDSLoader.cpp
//	Description :	DDSLoader implementation file.
//	Author		:	William McVicar
//  Mail		:	mcvicar.william@gmail.com
//

//  Precompiled Header
#include "CommonDefines.h"

//  Library Includes

//  Local Includes
#include "../ProteinFramework.h"
#include "../IFileSystem.h"
#include "../Image.h"

//  This includes
#include "ddsLoader.h"

//	Macros

//  Static Variables

//  Static Function Prototypes

//  Implementation

namespace Protein
{

bool CDDS::Load( const c8* _szFilename, Image& _rImage )
{
	std::string path;
	char buf[ PROTEIN_MAX_PATH ];
	strcpy( buf, _szFilename );
	strcat( buf, ".dds" );
	if( IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND == Protein::fileSystem().FileExists( buf, path ) )
		return false;

	FILE* pFile = NULL;
	if( !fileSystem().Open( path.c_str(), g_kBINARY_READ_ONLY, &pFile, true ) )
		return false;

	u32 fileSize = fileSystem().GetFileSize( pFile );
	
	if( fileSize < sizeof( tDDSHeader ) + sizeof( DWORD ) )
	{
		fileSystem().Close(pFile);
		return false;
	}

	byte* imgData = new byte[ fileSize ];
	if( !imgData )
	{
		fileSystem().Close( pFile );
		return false;
	}

	u32 bytesRead = fileSystem().Read( imgData, sizeof( byte ), fileSize, pFile ); 
	if( bytesRead != fileSize )
	{
		fileSystem().Close( pFile );
		delete [] imgData;
		return false;
	}

	fileSystem().Close( pFile );
	u32 uiMagic = *reinterpret_cast< u32* >( &imgData[0] );
	if( DDS_MAGIC != uiMagic)
	{
		delete [] imgData;
		return false;
	}

	tDDSHeader header = *reinterpret_cast< tDDSHeader* >( &imgData[0] + sizeof( u32 ) );
	if( header.dwSize != sizeof( tDDSHeader ) || header.ddspf.dwSize != sizeof( tDDSPixelFormat ) )
	{
		delete [] imgData;
		return false;
	}

	//Check to see if it has a dx10 extension
	u32 dx10FourCC = IFileSystem::MakeFourCC( 'D', 'X', '1', '0' );
	i32 dx10FlagCheck = header.ddspf.dwFlags & DDS_FOURCC;
	bool isDX10Header = false;
	if ( dx10FlagCheck && dx10FourCC == header.ddspf.dwFourCC )
    {
		if( fileSize < (sizeof(tDDSHeader)+sizeof(u32)+sizeof(tDDSHeaderDXT10)) )
        {
			delete [] imgData;
            return false;
        }

        isDX10Header = true;
    }

	i32 offset = sizeof( DWORD ) + sizeof( tDDSHeader );
	if( isDX10Header ) 
		offset += sizeof( tDDSHeaderDXT10 );

	ePixelFormat format = getPixelFormat( header );
	imgData += offset;
	i32 dataSize = fileSize - offset;
	_rImage.Init( header.dwWidth, header.dwHeight, header.dwDepth, format, header.dwMipMapCount, path, imgData, dataSize );

	imgData -= offset;
	delete [] imgData;

    return true;
}

ePixelFormat CDDS::getPixelFormat( const tDDSHeader& header )
{
	const unsigned int redMask	 = header.ddspf.dwRBitMask;
    const unsigned int greenMask = header.ddspf.dwGBitMask;
    const unsigned int blueMask	 = header.ddspf.dwBBitMask;
    const unsigned int alphaMask = header.ddspf.dwABitMask;

	//Define a macro to help with the if statement readability
	#define ISBITMASK( r,g,b,a )  ( redMask	  == r && \
									greenMask == g && \
									blueMask  == b && \
									alphaMask == a )

    if (header.ddspf.dwFlags & DDS_FOURCC )
    {
        // Compressed format
		const unsigned int fourCC = header.ddspf.dwFourCC;
        if (fourCC == fileSystem().MakeFourCC('D','X','T','1'))
            return PF_DXT1;
        else if (fourCC == fileSystem().MakeFourCC('D','X','T','3'))
            return PF_DXT3;
        else if (fourCC == fileSystem().MakeFourCC('D','X','T','5'))
            return PF_DXT5;
    }
    else if (header.ddspf.dwFlags & DDS_RGB)
    {
        if (header.ddspf.dwRGBBitCount == 32)
        {
            // 32-bit RGBA formats
            if(header.ddspf.dwFlags & DDS_ALPHA_PIXELS)
            {
                if( ISBITMASK( 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 ) )
                    return PF_BGRA8;

                if( ISBITMASK( 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ) )
                    return PF_RGBA8;
            }
        }
        else if (header.ddspf.dwRGBBitCount == 24)
        {
            // 24-bit RGB formats
            if( ISBITMASK( 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000) )
                return PF_BGR8;

            if( ISBITMASK( 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 ) )
                return PF_RGB8;
        }
        else if (header.ddspf.dwRGBBitCount == 16)
        {
            // 16-bit RGB and RGBA formats
            if( ISBITMASK( 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 ) )
                return PF_RGB565;
            
			if( ISBITMASK( 0x0000f800, 0x000007C0, 0x0000003e, 0x00000001 ) )
                return PF_RGBA5551;
            
			if( ISBITMASK( 0x00007C00, 0x000003e0, 0x0000001f, 0x00008000 ) )
                return PF_ARGB1555;
            
			if( ISBITMASK( 0x0000f000, 0x00000f00, 0x000000f0, 0x0000000f ) )
                return PF_RGBA4444;
            
			if( ISBITMASK( 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 ) )
                return PF_ARGB4444;
        }
    }
    else if (header.ddspf.dwFlags & DDS_ALPHA)
    {
        // 8-bit alpha format
        if (header.ddspf.dwRGBBitCount == 8)
			return PF_ALPHA8;
    }
    else
    {
        // 8-bit luminance format
        if( header.ddspf.dwRGBBitCount == 8 )
        {
            if ( ISBITMASK( 0x000000ff, 0x00000000, 0x00000000, 0x00000000 ) )
				return PF_LUMINANCE8;
        }

        // 8-bit luminance alpha format
        if (header.ddspf.dwRGBBitCount == 16 && ISBITMASK( 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00 ) )
			return PF_LUMINANCEALPHA8;
    }

    return PF_UNKNOWN;
}

}